/**
 * Original Author:		Michael Sawayda
 * Created:				8.21.2013
 * License:				Open Source
 * 
 **/

#include "D3D11Renderer.h"
#include <d3d11.h>
#include "Game.h"
#include <assert.h>

HWND								D3D11Renderer::hwnd;
CComPtr<ID3D11Device>				D3D11Renderer::d3dDevice = NULL;
CComPtr<ID3D11DeviceContext>		D3D11Renderer::d3dImmediateContext = NULL;
CComPtr<IDXGISwapChain>				D3D11Renderer::swapChain = NULL;
CComPtr<ID3D11RenderTargetView>		D3D11Renderer::renderTargetView[8];
CComPtr<ID3D11ShaderResourceView>	D3D11Renderer::shaderResourceView[8];

CComPtr<ID3D11Texture2D>			D3D11Renderer::depthStencilBuffer = NULL;
CComPtr<ID3D11DepthStencilView>		D3D11Renderer::depthStencilView = NULL;
CComPtr<ID3D11DepthStencilView>		D3D11Renderer::orthoDepthStencilView = NULL;
CComPtr<ID3D11DepthStencilState>	D3D11Renderer::depthStencilState = NULL;
CComPtr<ID3D11DepthStencilState>	D3D11Renderer::orthoDepthStencilState = NULL;
CComPtr<ID3D11RasterizerState>		D3D11Renderer::rasterStateBackfaceCulling = NULL;
CComPtr<ID3D11RasterizerState>		D3D11Renderer::rasterStateNoCulling = NULL;
CComPtr<ID3D11BlendState>			D3D11Renderer::blendState = NULL;
CComPtr<ID3D11RasterizerState>		D3D11Renderer::rasterizerState = NULL;
CComPtr<ID3D11Texture2D>			D3D11Renderer::renderTextures[7];

D3D11_VIEWPORT						D3D11Renderer::viewport;

bool								D3D11Renderer::vsyncEnabled = false;
int									D3D11Renderer::videoCardMemory = 0;
char								D3D11Renderer::videoCardDescription[128];

D3D_FEATURE_LEVEL					D3D11Renderer::supportedFeatureLevel;

bool D3D11Renderer::Initialize(HWND _hwnd, bool _fullscreen, bool _vsync, int _horizontalRes, int _verticalRes, bool _msaa)
{
	HRESULT hr;
	CComPtr<ID3D11Texture2D> backBufferPtr;
	D3D11_TEXTURE2D_DESC depthBufferDesc;
	D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
	D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
	D3D11_RASTERIZER_DESC rasterDesc;
	
	vsyncEnabled = _vsync;

	hwnd = _hwnd;
	UINT createDeviceFlags = 0;

#if defined(DEBUG) || defined(_DEBUG)
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_FEATURE_LEVEL featureLevels[6] = 
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
		D3D_FEATURE_LEVEL_9_3,
		D3D_FEATURE_LEVEL_9_2,
		D3D_FEATURE_LEVEL_9_1
	};

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory(&sd, sizeof(DXGI_SWAP_CHAIN_DESC));
	sd.BufferDesc.Width						= _horizontalRes;
	sd.BufferDesc.Height					= _verticalRes;
	sd.BufferDesc.Format					= DXGI_FORMAT_R8G8B8A8_UNORM;

	sd.SampleDesc.Count		= 8;
	sd.SampleDesc.Quality	= D3D11_STANDARD_MULTISAMPLE_PATTERN;

	sd.BufferUsage	= DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.BufferCount	= 1;
	sd.OutputWindow = hwnd;
	sd.Windowed		= _fullscreen;

	hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevels, 6, D3D11_SDK_VERSION, &sd, &swapChain, &d3dDevice, &supportedFeatureLevel, &d3dImmediateContext);

	if(supportedFeatureLevel != D3D_FEATURE_LEVEL_11_0)
	{
		printf("Direct3D Feature Level 11 unsupported.");
		return false;
	}

	//CREATING THE BLEND STATE
	D3D11_BLEND_DESC blendStateDescription;
	ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));

	blendStateDescription.IndependentBlendEnable = false;
	blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
	blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
	blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
	blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
	blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	blendStateDescription.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	
	D3D11Renderer::d3dDevice->CreateBlendState(&blendStateDescription, &blendState);

	hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr);
	if(FAILED(hr))
	{
		return false;
	}

	hr = d3dDevice->CreateRenderTargetView(backBufferPtr, NULL, &renderTargetView[0]);
	if(FAILED(hr))
	{
		return false;
	}

	ZeroMemory(&depthBufferDesc, sizeof(depthBufferDesc));
	depthBufferDesc.Width = _horizontalRes;
	depthBufferDesc.Height = _verticalRes;
	depthBufferDesc.MipLevels = 1;
	depthBufferDesc.ArraySize = 1;
	depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthBufferDesc.SampleDesc.Count = 8;
	depthBufferDesc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
	depthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthBufferDesc.CPUAccessFlags = 0;
	depthBufferDesc.MiscFlags = 0;

	hr = d3dDevice->CreateTexture2D(&depthBufferDesc, NULL, &depthStencilBuffer);
	if(FAILED(hr))
	{
		return false;
	}

	ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));

	depthStencilDesc.DepthEnable = true;
	depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;

	hr = d3dDevice->CreateDepthStencilState(&depthStencilDesc, &depthStencilState);
	if(FAILED(hr))
	{
		return false;
	}

	d3dImmediateContext->OMSetDepthStencilState(depthStencilState, 1);

	ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));

	depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
	depthStencilViewDesc.Texture2D.MipSlice = 0;

	hr = d3dDevice->CreateDepthStencilView(depthStencilBuffer, &depthStencilViewDesc, &depthStencilView);
	if(FAILED(hr))
	{
		return false;
	}

	rasterDesc.AntialiasedLineEnable = false;
	rasterDesc.CullMode = D3D11_CULL_BACK;
	rasterDesc.DepthBias = 0;
	rasterDesc.DepthBiasClamp = 0.0f;
	rasterDesc.DepthClipEnable = true;
	rasterDesc.FillMode = D3D11_FILL_SOLID;
	rasterDesc.FrontCounterClockwise = true;
	rasterDesc.MultisampleEnable = true;
	rasterDesc.ScissorEnable = false;
	rasterDesc.SlopeScaledDepthBias = 0.0f;

	hr = d3dDevice->CreateRasterizerState(&rasterDesc, &rasterStateBackfaceCulling);
	if(FAILED(hr))
	{
		return false;
	}

	d3dImmediateContext->RSSetState(rasterStateBackfaceCulling);

	viewport.Width = (float)_horizontalRes;
	viewport.Height = (float)_verticalRes;
	viewport.MinDepth = 0.0f;
	viewport.MaxDepth = 1.0f;
	viewport.TopLeftX = 0.0f;
	viewport.TopLeftY = 0.0f;	

	d3dImmediateContext->RSSetViewports(1, &viewport);

	d3dImmediateContext->OMSetBlendState(D3D11Renderer::blendState, 0, 0xffffffff);
	d3dImmediateContext->RSSetState(rasterStateBackfaceCulling);
	d3dImmediateContext->OMSetRenderTargets(1, &D3D11Renderer::renderTargetView[0].p, depthStencilView);

 	return true;
}

void D3D11Renderer::ClearScene(XMFLOAT4 _color)
{
	assert(d3dImmediateContext);
	assert(swapChain);

	for(int i = 0; i < 8; ++i)
	{
		if(renderTargetView[i])
		{
			d3dImmediateContext->ClearRenderTargetView(renderTargetView[i], reinterpret_cast<const float*>(&_color));
		}
	}
	
	d3dImmediateContext->ClearDepthStencilView(depthStencilView.p, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
}

void D3D11Renderer::ContextClearState(ID3D11DeviceContext* _context)
{
	_context->ClearState();

	d3dImmediateContext->RSSetState(rasterStateBackfaceCulling);
	d3dImmediateContext->RSSetViewports(1, &viewport);
	d3dImmediateContext->OMSetDepthStencilState(depthStencilState, 1);
}

void D3D11Renderer::Present(int _vBlankWait)
{
	assert(d3dImmediateContext);
	assert(swapChain);

	HRESULT hr = swapChain->Present(_vBlankWait, 0);
}

void D3D11Renderer::ResetDevice()
{

}
		
void D3D11Renderer::Shutdown()
{
	//CComPtr's are the shizznit
	//Nothing to see here!
}